package de.uni.muenster.idw2.location.bluetooth.service;


import java.util.ArrayList;
import java.util.List;

import de.uni.muenster.idw2.data.Position;
import de.uni.muenster.idw2.location.bluetooth.service.SynchroListUtils.BluetoothDataFilter;
import de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.core.DeficientDataException;
import de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.core.Fingerprinting;
import de.uni.muenster.idw2.utilities.Utils;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import java.util.concurrent.locks.LockSupport;

	public class PositionThread extends Thread {

		private Context context;
		private boolean isScanning = false;
		
		//List of constants for accessing the results
		public static String PositionX_FieldName = "positionX";
		public static String PositionY_FieldName = "positionY";
		public static String PositionZ_FieldName = "positionZ";
		public static String PositionAccuracy = "accuracy";
		
		//label for the parcel extra property to return the values used in the calculation.
		public static String CalcHostsList_FieldName = "HostsValues";
		
		private IComputeLocation algorithm;
		
		public synchronized void setPositioningMethod(PositioningMethod method){
		  //TODO != must be reset to ==
			if (method == PositioningMethod.pmFingerPrinting)
			{
			  try {
				algorithm = new Fingerprinting();
			} catch (LocatingInitializationException e) {
				
				//TODO Luis: Please handle this exception!
				
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			  Log.i("AMK", "Fingerprinting STARTED");
			}
		  else{
			  algorithm = new IdealLocationAlgorithm();
			  Log.i("AMK", "IdealLocationAlgorithm STARTED");
		  }
		  positioningMethod = method;
		}
		
		WGS84Position mLastPosition;
		PositioningMethod positioningMethod;
		
		//this is the actual List filled with the data if the sensor
		List<BluetoothDeviceData> devicesData;
		private int currentFloor = 0;
		
		public PositionThread(Context context, List<BluetoothDeviceData> devicesData, PositioningMethod method) {
			super();
			this.context = context;
			this.devicesData = devicesData;
			setPositioningMethod(method);
		}
		
		BluetoothDataFilter filter = new BluetoothDataFilter() {
			//the filter should determine which bluetooth data to use or exclude
			//i.e data from dongles placed in a different floor should not be considered in some method
			public boolean Filter(BluetoothDeviceData data) {
				if (positioningMethod == PositioningMethod.pmGeometric ){
				  //Geometric (Ideal Method) should not consider dongles form other floors	
				   if (data.getFloor() == currentFloor)
				     return true;
				   else return false; 
				}
				else  {
					//finger printing method does not care about it.. 
					return true;
				}
			}
		};
		
		
		@Override
		public void run() {
			mLastPosition = new WGS84Position(0,0,0,0);
			List<BluetoothDeviceData> devicesLocalData = new ArrayList<BluetoothDeviceData>();
			WGS84Position calculatedPoint;

			while (isScanning) {
				Intent mIntent = new Intent(Utils.LOCATION_UPDATE);
				
				//Use the thread safe function for copying the list of data, the filter in this case
				//is used to control which data from the list should used in the calculations.
				devicesLocalData = SynchroListUtils.doSynchronizedCopy(devicesData, filter); 
				
				try 
				{
					//TODO: put again the current floor..
					algorithm.setKnownFloor(getCurrentFloor());
					calculatedPoint = algorithm.computeLocation(devicesLocalData);
 
					mIntent.putExtra(PositionX_FieldName, calculatedPoint.getLongitude());
					mIntent.putExtra(PositionY_FieldName, calculatedPoint.getLatitude());
					mIntent.putExtra(PositionZ_FieldName, calculatedPoint.getFloor());
					mIntent.putParcelableArrayListExtra(CalcHostsList_FieldName,((ArrayList<BluetoothDeviceData>)devicesLocalData));
					
					//for setting the expected position object and sen
					mIntent.putExtra(Utils.LOCATION_UPDATE, new Position(calculatedPoint.getLongitude(), calculatedPoint.getLatitude(), (int)calculatedPoint.getFloor()));
					mIntent.setAction(Utils.RECEIVER_MSG);
					
					this.context.sendBroadcast(mIntent);
					mLastPosition = calculatedPoint;
					
					//Wait until new data is available. unpark() for this thread is done when new 
					//data is added to the allDiscoveriesBTDData due to new findings in discovery
					//check  PositioingService.updateAllDiscoveriesData
					LockSupport.park();
				}
				catch(DeficientDataException ex)
				{
					//the algorithms are going to rise these exceptions when there is not enough data
					//(i.e signal strenghts) for determining the position.
					//Log.e("PositionThread", "Deficient Data:" + ex.getMessage());
				}
				catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		}
		
		@Override
		public void start() {
			super.start();
			Log.i("AMK", "PositionThread start() called");
			this.isScanning = true;
		}
		
		public void startMeasurement(){
			Log.i("AMK", "PositionThread startMeasurement() called");
			this.isScanning = true;
			resume();
		}

		public void stopMeasurement() {
			Log.i("AMK", "PositionThread stopMeasurement() called");
			this.isScanning = false;
		}

		public synchronized void setCurrentFloor(int currentFloor) {
			this.currentFloor = currentFloor;
		}
		
		public synchronized int getCurrentFloor() {
			return currentFloor;
		}
	}

